package assist.datastore.dao;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import java.util.logging.Logger;

import assist.datastore.Datastore;
import assist.datastore.JDODatastore;
import assist.datastore.dao.exception.MultiplePlayersFoundException;
import assist.datastore.dao.exception.PlayerNotFoundException;
import assist.domain.model.Game;
import assist.domain.model.Player;
import assist.domain.model.PlayerAlias;
import assist.domain.model.PlayerYear;
import assist.domain.model.RosterPlayer;
import assist.domain.model.Team;

public class PlayerDAO extends EntityDAO<Player>
{
    private static Logger logger = 
        Logger.getLogger(PlayerDAO.class.getName());  

    public PlayerDAO(Datastore<Player> datastore)
    {
        super(datastore, Player.class);
    }
    
    public Player getByName(String name) throws MultiplePlayersFoundException, PlayerNotFoundException
    {
        PersistenceManager pm = JDODatastore.getPersistenceManager();
        try
        {
            name = name.toUpperCase();
            Query query = pm.newQuery(Player.class);
            query.setFilter("name.toUpperCase() == nameParam");
            query.declareParameters("String nameParam");                      
            //query.setUnique(true);
                       
            @SuppressWarnings("unchecked")
            Collection<Player> players = 
                 (Collection<Player>) query.execute(name);
            if (players.size() == 0)
            {                
                if (name.startsWith("MICHAEL"))
                {                    
                    return this.getByName(name.replaceAll("MICHAEL", "MIKE"));
                }
                if (name.startsWith("JAMES"))
                {                    
                    return this.getByName(name.replaceAll("JAMES", "JIM"));
                }  
                if (name.startsWith("ROBERT"))
                {                    
                    return this.getByName(name.replaceAll("ROBERT", "ROB"));
                }    
                if (name.startsWith("CHRISTOPHER"))
                {                    
                    return this.getByName(name.replaceAll("CHRISTOPHER", "CHRIS"));
                }                  
                return this.getByAlias(name);
            }            
            else if(players.size() > 1)
            {
                Set <Long> playerIds = new TreeSet<Long>();
                for (Player player : players)
                {
                    playerIds.add(player.getPlayerId());
                }
                throw new MultiplePlayersFoundException(name, playerIds);
            }           
            return pm.detachCopy(players.iterator().next());            
        }
        finally
        {
            pm.close();
        } 
    }
    
    public Set<Player> getPlayersInGames(Collection<Game> games, Team team)
    {
        PersistenceManager pm = JDODatastore.getPersistenceManager();
        try
        {        
        Collection<String> gameIds = new ArrayList<String>();        
        for(Game game: games)
        {
            gameIds.add(game.getId());
        }
        Query query = pm.newQuery(RosterPlayer.class);
        query.setResult("distinct player.playerId");
        query.setFilter("gamesParam.contains(gameId) && teamParam == team.abbreviation ");
        query.declareParameters("java.util.Collection gamesParam, String teamParam");        
        logger.fine("Executing RosterPlayer ID Query.");
        @SuppressWarnings("unchecked")
        Collection<Long> playerIds = 
            (Collection<Long>) query.execute(gameIds, team.getAbbreviation());   
        
        //FOR PERFORMANCE REASONS:
        //Takes forevor to detach the player list from the RosterPlayer table.
        //So just get the playerIds from Roster, and then grab them from 
        //Player Table.
        Query playerQuery = pm.newQuery(Player.class);
        playerQuery.declareParameters("java.util.Collection playerIdsParam");
        playerQuery.setFilter("playerIdsParam.contains(playerId)");
        playerQuery.setOrdering("name ascending");
        logger.fine("Executing Player Query.");
        @SuppressWarnings("unchecked")
        Collection<Player> players = 
            (Collection<Player>) playerQuery.execute(playerIds); 
        logger.fine("Detaching Players:" + players.size());
        Set<Player> detached = new LinkedHashSet<Player>(pm.detachCopyAll(players));     
        logger.fine("Done Detach:" + detached.size());
        return detached;

        }
        finally
        {
            pm.close();
        }        
    }

    public Player getPlayerByYear(Set<Long> playerIds, Integer year)
    {        
        PersistenceManager pm = JDODatastore.getPersistenceManager();
        try
        {        
       
        Query query = pm.newQuery(PlayerYear.class);
        query.setFilter("year == yearParam && playerId == playerIdParam");
        query.declareParameters("Integer yearParam,Long playerIdParam");
        //query.setUnique(true);
        for (Long playerId : playerIds)
        {
            @SuppressWarnings("unchecked")
            Collection<PlayerYear> playerYears = 
                 (Collection<PlayerYear>) query.execute(year, playerId);    
            if (playerYears.size() > 0)
            {
                return this.getById(playerYears.iterator().next().getPlayerId());
            }
        }
        return null;
        }
        finally
        {
            pm.close();
        }
        
    }

    public Player getByAlias(String name) throws PlayerNotFoundException, MultiplePlayersFoundException
    {
    
        PersistenceManager pm = JDODatastore.getPersistenceManager();
  
        try
        {        
        Query query = pm.newQuery(PlayerAlias.class);
       
        query.setFilter("name.toUpperCase() == nameParam");
        query.declareParameters("String nameParam");                      
        //query.setUnique(true);
        @SuppressWarnings("unchecked")
        Collection<PlayerAlias> players = 
             (Collection<PlayerAlias>) query.execute(name.toUpperCase());
        
        if (players.size() == 0)
        {
            logger.warning("!!!!!Player not found as Alias: " + name); 
            throw new PlayerNotFoundException(name);
        }            
        else if(players.size() > 1)
        {           
            logger.warning("!!!!!More than 1 player with Alias: " + name);
            Set <Long> playerIds = new TreeSet<Long>();
            for (PlayerAlias player : players)
            {
                playerIds.add(player.getPlayerId());
            }
            throw new MultiplePlayersFoundException(name, playerIds);
        }
        
        Long playerId = players.iterator().next().getPlayerId();
        if (playerId == null)
        {
            logger.warning("!!!!!Player not found as Alias: " + name); 
            throw new PlayerNotFoundException(name);
        }
        return this.getById(playerId);
        }
        finally
        {
            pm.close();
        }         
    }
}